//
//  HttpUtils.swift
//  puretennis
//
//  Created by IYE Technologies on 12/18/14.
//  Copyright (c) 2014 IYE Technologies. All rights reserved.
//

import UIKit
import SystemConfiguration

enum RequestMethod:String{
    case GET = "GET";
    case POST = "POST";
    case DELETE = "DELETE";
    case HEAD = "HEAD";
    case PUT = "PUT";
    case PATCH = "PATCH";
    case OPTIONS = "OPTIONS";
    case TRACE = "TRACE";
}

class HttpUtils: NSObject {
    
    class func sendRequest(requestURL:NSString,method:RequestMethod,httpBody:NSDictionary?,successHandle:((response:AnyObject?)->Void)?,errorHandle:((error:NSError?)->Void)?,requestHeaders:RequestHeader...){
        let isGet = (method == RequestMethod.GET);
        let url = Properties.HOST + requestURL;
        var resultObject: AnyObject?;
        var userGuid:AnyObject? = Utils.getValue("guid");
        var cacheAble = (isGet && userGuid != nil);
        if(userGuid == nil){
            userGuid = "";
        }
        let urlKey = url + (userGuid as String);
        var connectedToNetwork = isConnectedToNetwork();
        if(connectedToNetwork){
            let startHandleDate = NSDate();

            var encodingURL = url.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!;

            var nsURL = NSURL(string: encodingURL)!;

            // create the request
            var request = NSMutableURLRequest(URL: nsURL, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData, timeoutInterval: 30)

            //Set request method ('POST' or 'GET')
            request.HTTPMethod = method.rawValue;


            // Set token
            var token: AnyObject? = Utils.getValue("token");

            if(token != nil){
                var tokenAsString = token as String;
                request.addValue(tokenAsString, forHTTPHeaderField: "Access_token");
            }

            var cacheObject = Utils.getValue(urlKey) as? NSDictionary;
            if(cacheObject != nil && isGet){
                var etagValueAsString = cacheObject?.objectForKey("etag") as String
                request.addValue(etagValueAsString, forHTTPHeaderField: "If-None-Match");
            }

            //Set request header
            for requestHeader in requestHeaders{
                request.addValue(requestHeader.getValue(), forHTTPHeaderField: requestHeader.getField());
            }
            
            request.addValue("application/json", forHTTPHeaderField: "Content-Type");

            if(httpBody != nil){
                var data =  NSJSONSerialization.dataWithJSONObject(httpBody!, options: NSJSONWritingOptions.PrettyPrinted, error: nil);
                request.HTTPBody = data;
            }

            let session = NSURLSession.sharedSession();

            let task = session.dataTaskWithRequest(request) { (data: NSData!, response: NSURLResponse!, error: NSError!) in
                if (error != nil) {
                    Utils.debug("Error happens when sending out request [\(requestURL)]");
                    Utils.debug(error)
                    if(cacheAble){
                        var cache:AnyObject? = Utils.getValue(urlKey);
                        resultObject = cache;
                        if(cache != nil){
                            resultObject = (cache as NSDictionary).objectForKey("response");
                        }
                    }
                }else{
                    var httpURLResponse = response as NSHTTPURLResponse;
                    var statusCode = httpURLResponse.statusCode;
                    var headers = httpURLResponse.allHeaderFields as NSDictionary;
                    var consumeTime = DateUtils.timeInterval(startHandleDate, to: NSDate());
                    Utils.debug("[\(method.rawValue)] [\(consumeTime) ms] [\(statusCode)] \(nsURL)");
                    switch statusCode{
                    case 200:
                        resultObject = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil);
                        if(resultObject == nil){
                            resultObject = NSString(data: data, encoding: NSUTF8StringEncoding);
                        }
                        if(cacheAble){
                            var etag: String = isGet ? headers.objectForKey("Etag") as String : ""
                            var cacheData = NSMutableDictionary();
                            cacheData.setObject(resultObject!, forKey: "response")
                            cacheData.setObject(etag, forKey: "etag")
                            Utils.saveData(cacheData, key: urlKey);
                        }
                        break;
                    default:
                        if(cacheAble){
                            var cache:AnyObject? = Utils.getValue(urlKey);
                            resultObject = cache;
                            if(cache != nil){
                                resultObject = (cache as NSDictionary).objectForKey("response");
                            }
                        }
                        break;
                    }
                }
                dispatch_async(dispatch_get_main_queue(), {
                    if(resultObject != nil){
                        successHandle!(response: resultObject);
                    } else {
                        errorHandle?(error: error);
                    }
                });
            }
            task.resume()
        } else {
            if(cacheAble){
                var cache:AnyObject? = Utils.getValue(urlKey);
                resultObject = cache;
                if(cache != nil){
                    resultObject = (cache as NSDictionary).objectForKey("response");
                }
                if(resultObject != nil){
                    successHandle!(response: resultObject);
                } else {
                    errorHandle?(error: nil);
                }
            } else {
                errorHandle?(error: nil);
            }
        }
    }

    class func isConnectedToNetwork() -> Bool {
        var reachability = Reachability.reachabilityForInternetConnection();
        var networkStatus = reachability.currentReachabilityStatus();
        return (networkStatus.value != NotReachable.value);
    }
    
    class func sendSynchronousRequest(url:String,method:RequestMethod,requestBody:NSDictionary?,headers:RequestHeader...) -> AnyObject?{
        let startHandleDate = NSDate();
        let isGet = (method == RequestMethod.GET);
        var url = Properties.HOST + url;
        var nsURL = NSURL(string: url)!;
        
        var urlKey:String!;
        var userGuid:AnyObject? = Utils.getValue("guid");
        if(userGuid != nil){
            urlKey = url + (userGuid as String);
        }
        
        var enableCache = (isGet && userGuid != nil);
        
        var connectedToNetwork = isConnectedToNetwork();
        if(!connectedToNetwork && enableCache){
            var cache:AnyObject? = Utils.getValue(urlKey);
            var resultObject: AnyObject? = cache;
            if(cache != nil){
                resultObject = (cache as NSDictionary).objectForKey("response");
            }
            return resultObject;
        }
        
        var request = NSMutableURLRequest(URL: nsURL, cachePolicy: NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData, timeoutInterval: 30);
        
        if(enableCache){
            var cacheObject = Utils.getValue(urlKey) as? NSDictionary;
            if(cacheObject != nil){
                let etagValueAsString = cacheObject?.objectForKey("etag") as String
                request.addValue(etagValueAsString, forHTTPHeaderField: "If-None-Match");
            }
        }
        
        request.HTTPMethod = method.rawValue;
        request.addValue("application/json", forHTTPHeaderField: "Content-Type");
        // Set token
        var token: AnyObject? = Utils.getValue("token");
        if(token != nil){
            var tokenAsString = token as String;
            request.addValue(tokenAsString, forHTTPHeaderField: "Access_token");
        }
        
        for header in headers{
            request.addValue(header.getValue(), forHTTPHeaderField: header.getField());
        }
        
        if(requestBody != nil){
            let data =  NSJSONSerialization.dataWithJSONObject(requestBody!, options: NSJSONWritingOptions.PrettyPrinted, error: nil);
            request.HTTPBody = data;
        }
        
        var response:NSURLResponse?;
        let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: nil);
        
        if(response != nil){
            var resultObject: AnyObject?;
            
            var httpResponse = response as NSHTTPURLResponse;
            var statusCode = httpResponse.statusCode;
            let consumeTime = DateUtils.timeInterval(startHandleDate, to: NSDate());
            Utils.debug("[\(method.rawValue)] [\(consumeTime) ms] [\(statusCode)] \(nsURL)");
            if(statusCode == 200){
                let headers = httpResponse.allHeaderFields as NSDictionary;
                if(data != nil){
                    resultObject = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: nil);
                    if(resultObject == nil){
                        resultObject = NSString(data: data!, encoding: NSUTF8StringEncoding);
                    }
                }
                if(enableCache){
                    var etag: String = isGet ? headers.objectForKey("Etag") as String : "";
                    var cacheData = NSMutableDictionary();
                    cacheData.setObject(resultObject!, forKey: "response")
                    cacheData.setObject(etag, forKey: "etag")
                    Utils.saveData(cacheData, key: urlKey);
                }
            }else{
                if(enableCache){
                    var cache:AnyObject? = Utils.getValue(urlKey);
                    resultObject = cache;
                    if(cache != nil){
                        resultObject = (cache as NSDictionary).objectForKey("response");
                    }
                }
            }
            return resultObject;
        } else {
            return nil;
        }
    }

}
